import fs from 'fs-extra';
import path from "path";
import { BaseTask, TaskResult } from "../../common//tasks/BastTask.js";
import { toolchain } from '../../toolchain.js';
import { Cmd } from '../../tools/Cmd.js';
import { ECertType, IProvision, PlistHelper } from '../../tools/PlistHelper.js';
import { SSHClient } from '../../tools/SSHClient.js';
import { Nullable } from '../../typings';
import { PrebuildAppData, PreBuildAppTask } from '../../common/tasks/PreBuildAppTask.js';
import { WrapCSharpTask } from '../../common/tasks/WrapCSharpTask.js';

export class IosBuildUnityTask extends BaseTask<void> {
    async run(): Promise<TaskResult<void>> {
        const xsdkPath = path.join(this.cmdOption.oldToolsRoot, 'sdk/ios');
        const iosCertPath = path.join(this.cmdOption.oldToolsRoot, 'iosCert');

        const channelCfg = toolchain.params.channelCfg;

        const prebuildResult = toolchain.taskHelper.getResult<PrebuildAppData>(PreBuildAppTask.name);
        
        const versionCode = prebuildResult!.data!.versionCode;
        const fullVersion = toolchain.params.version + '.' + versionCode;
		
        console.log('@@ 导出XCode工程');
        const exportParams = {
            sdkModPath: channelCfg.iosSdkMod ? path.join(xsdkPath, channelCfg.iosSdkMod) : '',
            mainModPath: path.join(xsdkPath, 'MainMod')
        };
        
        await fs.writeFile(path.join(toolchain.params.workSpacePath, 'Assets/Editor/XCodeBuilder/exportParams.json'), JSON.stringify(exportParams), 'utf-8');
        await fs.writeFile(path.join(toolchain.params.workSpacePath, 'Assets/Editor/XCodeBuilder/config.json'), JSON.stringify(channelCfg['ios-sdk-params']), 'utf-8');
        await this._exportIosProject(prebuildResult!.data!.defines, versionCode);
        await this.fixXCodeProject();

        console.log('@@ 签名工程并打包');
        const certPath = path.join(iosCertPath, channelCfg.bundleId);
        const devp12Path = path.join(certPath, 'dev.p12');
        const disp12Path = path.join(certPath, 'dis.p12');
        
        let devp12password = '';
        let disp12password = '';
        if (fs.existsSync(path.join(certPath, 'dev.txt'))) {
            devp12password = await fs.readFile(path.join(certPath, 'dev.txt'), 'utf-8');
            console.log('@@ devp12password:'+devp12password);
        }
        if (fs.existsSync(path.join(certPath, 'dis.txt'))) {
            disp12password = await fs.readFile(path.join(certPath, 'dis.txt'), 'utf-8');
            console.log('@@ disp12password:'+disp12password);
        }

        const devprovisionPath = path.join(certPath, 'dev.mobileprovision');
        const appstoreprovisionPath = path.join(certPath, 'appstore.mobileprovision');
        const adhocprovisionPath = path.join(certPath, 'adhoc.mobileprovision');

        let buildtimes = 0;
        if (fs.existsSync(devp12Path) && fs.existsSync(devprovisionPath)) {
            console.log('@@ 生成Ipa for dev');
            buildtimes++;
            await this._buildSignedIpa(devp12Path, devp12password, devprovisionPath, ECertType.Dev, 'dev', fullVersion);
        }
        if (fs.existsSync(disp12Path) && fs.existsSync(appstoreprovisionPath)) {
            console.log('@@ 生成Ipa for appstore');
            buildtimes++;
            await this._buildSignedIpa(disp12Path, disp12password, appstoreprovisionPath, ECertType.Store, 'appstore', fullVersion);
        }
        if (fs.existsSync(disp12Path) && fs.existsSync(adhocprovisionPath)) {
            console.log('@@ 生成Ipa for adhoc');
            buildtimes++;
            await this._buildSignedIpa(disp12Path, disp12password, adhocprovisionPath, ECertType.AdHoc, 'adhoc', fullVersion);
        }
        if (buildtimes == 0)
            await this._buildSignedIpa('', '', '', ECertType.Invalid, '', fullVersion);

        return { success: true, errorCode: 0 };
    }

    private async _exportIosProject(defines: string, buildNumber: string): Promise<void> {
        const exportpath = path.join(toolchain.params.workSpacePath, '../exportxcode');
        await fs.remove(exportpath);

        const params = ['-batchmode', '-projectPath', toolchain.params.workSpacePath, '-executeMethod', 'BuildCommand.BuildForIOS',
            '-defines', defines, '-bundleVersion', toolchain.params.channelCfg.bundleVersion!, '-buildNumber', buildNumber, '-bundleId', toolchain.params.channelCfg.bundleId, 
            '-productName', toolchain.params.channelCfg.productName.replace(/ /g, '%20'), '-quit'];
        await toolchain.unity.runUnityCommand(this.cmdOption, params, 'IOS');
    }

    private async fixXCodeProject(): Promise<void> {
        // puerts xll2cpp模式需要拷贝一些头文件
        const unityRoot = path.dirname(toolchain.params.unityTool!);
        const il2cppConfigH = path.join(toolchain.params.workSpacePath, '../exportxcode/Libraries/libil2cpp/include/il2cpp-config.h');
        if (fs.existsSync(il2cppConfigH)) {
            const content = await fs.readFile(il2cppConfigH, 'utf-8');
            if (content.includes('#define IL2CPP_USE_SPARSEHASH (IL2CPP_TARGET_ANDROID || IL2CPP_TARGET_IOS)')) {
                // 修复xcode打包报错：'../../external/google/sparsehash/sparse_hash_map.h' file not found
                const copySrc = path.join(unityRoot, 'Data/il2cpp/external/google/sparsehash');
                if (fs.existsSync(copySrc)) {
                    const copyDst = path.join(toolchain.params.workSpacePath, '../exportxcode/Libraries/external/google/sparsehash');
                    await fs.ensureDir(copyDst);
                    await fs.copy(copySrc, copyDst, { recursive: true });                    
                } else {
                    console.log('il2cpp files not exist, skip copy:', copySrc);
                }
            } else {
                console.log('IL2CPP_USE_SPARSEHASH not defined, skip copy');
            }
        } else {
            console.log('il2cpp-config.h not exists, skip copy:', il2cppConfigH);
        }
    }

    private async _buildSignedIpa(p12Path: string, p12Password: string, provisionPath: string, certType: ECertType, certString: string, fullVersion: string): Promise<void> {
        const channelCfg = toolchain.params.channelCfg;
        
        const xcodePath = path.join(toolchain.params.workSpacePath, '../exportxcode');
        const localTarFile = path.join(toolchain.params.workSpacePath, '../exportxcodetgz.tgz');
        await fs.remove(localTarFile);
            
        if (p12Path) {
            console.log('@@ 对已导出项目进行二次签名:', xcodePath);
            const plistContent = await fs.readFile(provisionPath, 'utf-8');
            const plistObj = await PlistHelper.read(plistContent) as unknown as IProvision;
            channelCfg.sign = plistObj.sign;
            channelCfg.provisioning = plistObj.provisioning;
            channelCfg.companyName = plistObj.companyName;
            channelCfg.team = plistObj.team;
            channelCfg.provisioningUUID = plistObj.provisioningUUID;

            await PlistHelper.writeProvision(path.join(xcodePath, 'export.plist'), channelCfg.provisioning, channelCfg.bundleId, certType)
            const exportParams = {
                provisioning: channelCfg.provisioning,
                sign: channelCfg.sign,
                team: channelCfg.team
            };
            await fs.writeFile(path.join(toolchain.params.workSpacePath, 'Assets/Editor/XCodeBuilder/signConfig.json'), JSON.stringify(exportParams));
            await this._signXCodeProject();
        }
        console.log('@@ 压缩工程:', xcodePath);
        await new Cmd().run('tar', ['-C', xcodePath, '-czvf', localTarFile, '.']);
        
        const hostworkspace = toolchain.params.macWorkspace + channelCfg.ipa + (certString ? "." + certString : "");
        const serverP12Path = hostworkspace + "/" + path.basename(p12Path);
        const serverProvisionPath = hostworkspace + "/" + path.basename(provisionPath);
        const serverTarFile = hostworkspace + '/exportxcodetgz.tgz';
        const serverXcodePath = hostworkspace + '/exportxcode';
        const serverExportPath = hostworkspace + '/ipaexport';
        const serverArchivePath = hostworkspace + '/game.xcarchive';
        const serverIpaPath = serverExportPath + '/Unity-iPhone.ipa';
        const localIpaPath = path.join(toolchain.params.workSpacePath, '../Unity-iPhone.ipa');
        await fs.remove(localIpaPath);
        const apkName = channelCfg.bundleId + ".v" + fullVersion + "." + certString + ".ipa";
        
        const hostinfo = {
            host: toolchain.params.macHost,
            port: toolchain.params.macPort,
            username: toolchain.params.macUsername,
            password: toolchain.params.macPassword
        };
        console.log('@@ 开始连接远程机器:', hostinfo.host);
        const ssh = new SSHClient();
        const connectResult = await ssh.connect(hostinfo);
        if (connectResult === 0) {
            console.log('@@ 成功连接目标机器:', hostinfo.host);
            // 检查服务器目录，创建项目专属目录
            await ssh.exec(`rm -rf ${hostworkspace}`);
            await ssh.exec(`mkdir -p ${hostworkspace}`);
            console.log('@@ 创建工作文件夹:', hostworkspace);
            
            if (p12Path) {
                await ssh.put(p12Path, serverP12Path);
                console.log('@@ copy p12 from:', p12Path, "to:", serverP12Path);
                await ssh.put(provisionPath, serverProvisionPath);
                console.log('@@ copy provision from:', provisionPath, "to:", serverProvisionPath);

                // 将p12Path和provisionPath拷贝到目标机器
                await ssh.exec("security unlock-keychain -p \"" + hostinfo.password + "\" ~/Library/Keychains/login.keychain-db");
                await ssh.exec("security import " + serverP12Path + " -k ~/Library/Keychains/login.keychain -P " + p12Password + " -T /usr/bin/codesign");
                await ssh.exec("security set-key-partition-list -S apple-tool:,apple: -s -k " + hostinfo.password + " ~/Library/Keychains/login.keychain-db");
                await ssh.exec("cp " + serverProvisionPath + " ~/Library/MobileDevice/'Provisioning Profiles'/" + channelCfg.provisioningUUID + ".mobileprovision");
                console.log('@@ 签名文件成功导入目标机器');
            }            
            
            // 发送工程到目标机器
            const putResult = await ssh.put(localTarFile, serverTarFile);
            console.log('@@ 项目发送到目标机器，result:', putResult);
            const mkdirResult = await ssh.exec(`mkdir -p ${serverXcodePath}`);
            console.log('@@ mkdir，result:', mkdirResult);
            const tarResult = await ssh.exec(`tar -xzf ${serverTarFile} -C ${serverXcodePath}`);
            console.log('@@ tar, result:', tarResult);
            const chmodResult = await ssh.exec('chmod -R 777 ' + serverXcodePath)
            console.log('@@ 项目成功解压', chmodResult);

            // 删除文件
            await ssh.exec(`rm -f ${serverTarFile}`);
            
            // console.log('@@ 工程执行archive');
            // const achiveResult = await ssh.exec('xcodebuild archive -project ' + serverXcodePath + '/Unity-iPhone.xcodeproj -scheme Unity-iPhone -archivePath ' + serverArchivePath);
            // console.log('archive result:', achiveResult);
            // console.log('@@ 工程执行exportArchive');
            // const exportArchiveResult = await ssh.exec('xcodebuild -exportArchive -archivePath ' + serverArchivePath + ' -exportPath ' + serverExportPath+' -exportOptionsPlist ' + serverXcodePath+'/export.plist');
            // console.log('export archive result:', exportArchiveResult);
            // console.log('@@ 工程打包完毕，开始传回ipa文件');
            // await ssh.exec('mv ' + serverExportPath + "/*.ipa" + " " + serverIpaPath);
            // await ssh.get(serverIpaPath, localIpaPath);
            // console.log('@@ ipa已拷贝到本地：' + localIpaPath);
            // await this._copyIpa2UploadDir(localIpaPath, apkName);
        } else {
            console.error('@@ 连接目标机器失败:', hostinfo.host);
        }
        await fs.remove(localTarFile);
        console.log('@@ ipa打包完毕');
        await ssh.end();
    }
    
    private async _signXCodeProject(): Promise<void> {
        const params = ['-batchmode', '-projectPath', toolchain.params.workSpacePath, '-executeMethod', 'XCodePostProcess.SignXCodeProject', '-quit'];
        await toolchain.unity.runUnityCommand(this.cmdOption, params, 'SignXCodeProject');
    }

    private async _copyIpa2UploadDir(ipaPath: string, apkName: string): Promise<void> {
        const uploadDir = path.join(toolchain.params.uploadPath, 'ipa', toolchain.params.channelCfg.path);
        await fs.ensureDir(uploadDir);
        console.log('@@ 开始生成.ipa下载地址');
        console.log('uploadDir:', uploadDir);
        console.log("-----ipaPath--: ", ipaPath, "-----apkName:", apkName);
        if (!fs.existsSync(ipaPath)) {
            console.log("error: not find ipa, build failed!", ipaPath);
            return;
        }
        await fs.ensureDir(toolchain.params.packageLocalWebPath);
        // 将apk保存到hudson，供内网下载
        const apkTarget = path.join(toolchain.params.packageLocalWebPath, apkName);
        await fs.copy(ipaPath, apkTarget);

        // 生成apk下载配置文件
        await fs.copy(ipaPath, path.join(uploadDir, apkName));

        const apkStat = await fs.stat(apkTarget);
        const size = String(Math.floor(apkStat.size / (1024 * 1024)));
        console.log('ipa size:' + size);
    }

    get dependencies(): Nullable<string[]> {
        return [WrapCSharpTask.name, PreBuildAppTask.name];
    }

    get skip(): boolean {
        return !this.cmdOption.buildExe;
    }
}